package net.siufung.security.provider;

import cn.hutool.core.util.ObjectUtil;
import com.google.common.collect.Lists;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import net.siufung.core.result.ResultFail;
import net.siufung.security.api.properties.SecurityProperties;
import net.siufung.security.api.properties.SecurityProperties.AccessEncrypt;
import net.siufung.security.api.service.ICurrentUserPasswdService;
import net.siufung.security.base.CurrentUser;
import net.siufung.security.base.service.ICurrentUserService;
import net.siufung.security.provider.dto.PasswdLoginDto;
import net.siufung.security.provider.exception.PasswdAuthenticationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 密码授权方式
 * @author 陈建峰
 * @since 2022/3/2 1:29 下午
 */
@Slf4j
public class PasswdAuthenticationProvider implements AuthenticationProvider, InitializingBean {

    @Setter
    private ICurrentUserService currentUserService;

    @Setter
    private ICurrentUserPasswdService loginPasswordService;

    @Setter
    private SecurityProperties securityProperties;

    @Override
    public void afterPropertiesSet() {
        if(Objects.isNull(currentUserService)){
            throw new PasswdAuthenticationException("currentUserService is null");
        }
        if(Objects.isNull(loginPasswordService)){
            throw new PasswdAuthenticationException("loginPasswordService is null");
        }
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        PasswdAuthenticationToken passwdAuthenticationToken = (PasswdAuthenticationToken) authentication;
        PasswdLoginDto passwdLoginDto = passwdAuthenticationToken.getPasswdLoginDto();
        CurrentUser currentUser = currentUserService
                .getUserByLoginName(passwdLoginDto.getLoginName(), passwdLoginDto.getTenantId());
        if(!Objects.isNull(currentUser)){
            additionalAuthenticationChecks(currentUser, passwdLoginDto);
            List<String> authorities = currentUserService.getAuthorities(currentUser.getUsername());
            List<GrantedAuthority> grantedAuthorityList = Lists.newArrayList();
            if(ObjectUtil.isNotEmpty(authorities)){
                grantedAuthorityList = authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
            }
            return this.createSuccessAuthentication(currentUser, authentication, grantedAuthorityList);
        }
        throw new PasswdAuthenticationException(ResultFail.AUTH_USERNAME_NOT_FOUND);
    }

    protected void additionalAuthenticationChecks(
            CurrentUser currentUser,
            PasswdLoginDto passwdLoginDto) throws AuthenticationException {
        //可以对密码加密(通过配置来决定)
        AccessEncrypt accessEncrypt = securityProperties.getEncrypt();
        if(!Objects.isNull(accessEncrypt) && accessEncrypt.isPassword()){
            String accessPasswordType = accessEncrypt.getPasswordType();
        }
        String presentedPassword = passwdLoginDto.getLoginPasswd();
        if (!loginPasswordService.matches(presentedPassword, currentUser.getPassword())) {
            throw new PasswdAuthenticationException(ResultFail.AUTH_INVALID_PASSWORD);
        }
    }

    protected Authentication createSuccessAuthentication(
            CurrentUser currentUser,
            Authentication authentication,
            List<GrantedAuthority> grantedAuthorityList){
        PasswdAuthenticationToken passwdAuthenticationToken = new PasswdAuthenticationToken(
                currentUser,
                currentUser.getPassword(),
                grantedAuthorityList
        );
        passwdAuthenticationToken.setDetails(authentication.getDetails());
        return passwdAuthenticationToken;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(PasswdAuthenticationToken.class);
    }
}
